package com.huan.bean;

import com.huan.anno.Bean;
import com.huan.anno.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class MyApplicationContext implements ApplicationContext {
    //创建一个map集合，放bean对象
    private Map<Class,Object> beanFactory = new HashMap<>();
    private static String rootPath;

    //返回对象
    @Override
    public Object getBean(Class cla) {
        return beanFactory.get(cla);
    }

    //创建有参构造，传递包路径 设置包扫描规则
    //当前包及其子包，哪个类有@Bean注解，把这个类通过反射实例化
    public MyApplicationContext(String basePackage) {
        //com.huan.spring....
        try {
            // 1. 把“.”替换成"\"
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            // 2. 获取包绝对路径
            //currentThread()获取当前线程
            //getContextClassLoader()获取类加载器
            //getResources(packagePath)获取绝对路径
            Enumeration<URL> urls =
                    Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();//绝对路径
                //转码 得到完整的绝对路径
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面路径部分，字符串截取
                rootPath = filePath.substring(0,filePath.length()-packagePath.length());
                //包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 完成属性注入
        loadDi();
    }

    //属性注入
    private void loadDi() {
        //实例化对象在beanFactory的map集合中
        //1. 遍历beanFactory
        //private Map<Class,Object> beanFactory = new HashMap<>();
        Iterator<Class> iterator = beanFactory.keySet().iterator();
        while(iterator.hasNext()){
            Class key = iterator.next();
            //2. 获取map集合每个对象（value），获取每个对象的属性
            Object value = beanFactory.get(key);
            System.out.println("key:"+key+",value:"+value);
            Class cla = value.getClass();
            Field[] declaredFields = cla.getDeclaredFields();
            //3. 遍历得到的每个对象的属性数组，得到每个属性
            for (Field f:declaredFields) {
                //4. 判断属性上是否有@Di注解
                if (f.isAnnotationPresent(Di.class)){
                    //如果属性私有，需要设置值
                    f.setAccessible(true);
                    //5. 如果有注解，把对象设置（注入）
                    try {
                        //set(value,beanFactory.get(f.getType()))
                        f.set(value,beanFactory.get(f.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    // 包扫描 加载bean，实例化
    private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1. 判断当前内容是否是文件夹
        if (file.isDirectory()){
            //2. 获取文件夹的所有内容
            File[] childFiles = file.listFiles();
            //3. 判断文件夹为空，则直接返回
            if (childFiles == null && childFiles.length == 0){
                return;
            }
            //4. 如果文件夹不为空，则遍历文件夹所有内容
            for (File f:childFiles) {
                //4.1 遍历得到每个file对象，继续判断，如果还是文件夹，递归
                if (f.isDirectory()){
                    //递归
                    loadBean(f);
                }else {//4.2 遍历得到的不是文件夹，是文件
                    //4.3 得到包路径+类名称部分 字符串截取
                    String pathWithClass = f.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4 判断当前文件的类型是否为.class
                    if (pathWithClass.contains(".class")){
                        //4.5 如果为.class类型，把路径\替换成. 把.class去掉
                        String allName = pathWithClass.replaceAll("\\\\", "\\.");
                        allName = allName.substring(0,allName.length()-".class".length());
                        //4.6.1 获取类的class
                        Class<?> cla = Class.forName(allName);
                        //4.6.2 判断不是接口
                        if (!cla.isInterface()){
                            //4.6 判断类上面是否有@Bean注解，如果有实例化过程
                            Bean annotation = cla.getAnnotation(Bean.class);
                            if (annotation != null){
                                //4.6.4 实例化
                                Object instance = cla.getConstructor().newInstance();
                                //4.7 把对象实例化之后，放到map集合beanFactory
                                //4.7.1 判断当前类如果有接口，让接口class作为map的key
                                if(cla.getInterfaces().length>0){
                                    beanFactory.put(cla.getInterfaces()[0],instance);
                                }else {
                                    beanFactory.put(cla,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
